package cn.xenosp.server.common.sslApply;

import cn.xenosp.server.common.error.HttpsApplyException;
import lombok.extern.slf4j.Slf4j;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Date;

@Slf4j
public class CertbotManager {

    private static final String CERTBOT_PATH = "certbot"; // 确保certbot在系统的PATH中，或者指定完整路径
    private static final String CERTIFICATE_PATH = "fullchain.pem";
    private static final String CERTIFICATE_DIRECTORY = "/etc/letsencrypt/live"; // 默认证书目录
    private static String SCRIPT_PATH = "/data/webroot/service-web/WEB-INF/classes/sh/certbotManager.sh";
    private static CertbotManager certbotManager;
    private CertbotManager() {
        if (!isCertbotInstalled()) {
            throw new HttpsApplyException("请安装 Certbot");
        }
        if (!addExecutePermission()) {
            throw new HttpsApplyException("添加执行权限时出错");
        }
    }

    public static CertbotManager getInstance() {
        if (certbotManager == null) {
            certbotManager = new CertbotManager();
        }
        return certbotManager;
    }

    public static void setScriptPath(String scriptPath) {
        SCRIPT_PATH = scriptPath;
    }

    /**
     * 申请证书
     * @param domain 域名
     * @return 操作是否成功
     * @throws IOException 如果执行命令时发生错误
     * @throws RuntimeException 如果certbot未安装
     */
    public boolean requestCertificate(String domain) throws IOException {
//        Process process = Runtime.getRuntime().exec("certbot certonly -d "+domain+" --manual --preferred-challenges dns --manual-auth-hook \"alidns\" --manual-cleanup-hook \"alidns clean\"");
        // 创建 ProcessBuilder 对象，指定要执行的命令和参数
        ProcessBuilder processBuilder = new ProcessBuilder("sh", SCRIPT_PATH, domain);

        // 启动进程
        Process process = processBuilder.start();
        readProcessOutput(process);

        return process.exitValue() == 0;
    }

    /**
     * 续签证书
     * @return 操作是否成功
     * @throws IOException 如果执行命令时发生错误
     * @throws RuntimeException 如果certbot未安装
     */
    public boolean renewCertificate(String domain) throws IOException {
        Process process = Runtime.getRuntime().exec("certbot renew ");
        readProcessOutput(process);
        return process.exitValue() == 0;
    }

    /**
     * 查看证书有效期
     * @param domain 域名
     * @return 证书有效期
     * @throws IOException 如果读取文件时发生错误
     * @throws CertificateException 如果解析证书时发生错误
     */
    public CertificateInfo getCertificateExpiryDate(String domain) throws IOException, CertificateException {
        Path path = Paths.get(CERTIFICATE_DIRECTORY, domain, CERTIFICATE_PATH);
        if (!Files.exists(path)) {
            throw new IOException("证书文件不存在: " + path.toString());
        }
        byte[] certBytes = Files.readAllBytes(path);
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        X509Certificate cert = (X509Certificate) cf.generateCertificate(new java.io.ByteArrayInputStream(certBytes));
        Date notBefore = cert.getNotBefore();
        Date notAfter = cert.getNotAfter();
        return new CertificateInfo(notBefore, notAfter);
    }

    /**
     * 下载证书
     * @param domain 域名
     * @return 下载链接
     * @throws IOException 如果读取或写入文件时发生错误
     */
    public Path downloadCertificate(String domain) throws IOException {
        Path path = Paths.get(CERTIFICATE_DIRECTORY, domain, CERTIFICATE_PATH);
        if (!Files.exists(path)) {
            throw new IOException("证书文件不存在: " + path.toString());
        }
        // 返回下载链接
        return path;
    }

    private void readProcessOutput(Process process) throws IOException {
        try{
            // 读取脚本的标准输出
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line;
            while ((line = reader.readLine()) != null) {
                log.info(line);
            }

            // 读取脚本的错误输出
            BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
            while ((line = errorReader.readLine()) != null) {
                log.error(line);
            }

        }catch (Exception e){
            e.printStackTrace();
        }
        int exitCode;
        try {
            exitCode = process.waitFor();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new IOException("Process was interrupted", e);
        }
        if (exitCode != 0) {
            throw new IOException("Process exited with error code " + exitCode);
        }
    }

    /**
     * 检查certbot是否安装
     * @return true 如果certbot已安装，否则false
     */
    private boolean isCertbotInstalled() {
        try {
            Process process = Runtime.getRuntime().exec(new String[]{CERTBOT_PATH, "--version"});
            return process.waitFor() == 0;
        } catch (IOException | InterruptedException e) {
            return false;
        }
    }
    /**
     * 添加脚本执行权限
     */
    public boolean addExecutePermission() {
        try {
            log.info(SCRIPT_PATH);
            // 构建 chmod 命令
            ProcessBuilder processBuilder = new ProcessBuilder("chmod", "+x", SCRIPT_PATH);
            // 启动进程
            Process process = processBuilder.start();
            readProcessOutput(process);
            // 等待命令执行完成并获取退出状态码
            return process.exitValue() == 0;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
}